package com.bargate.domain.spring.expression;

import com.bargate.domain.spring.expression.configuration.ConfigurationEnvConverters;
import com.bargate.domain.spring.expression.configuration.ConfigurationForClassOperations;
import com.bargate.domain.spring.expression.configuration.ConfigurationForPropertyAccess;
import com.bargate.domain.spring.expression.configuration.ConfigurationGeneralBeans;
import com.bargate.domain.spring.expression.value.*;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.*;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StopWatch;

import java.awt.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * Created by IntelliJ IDEA.
 * User: Eldad.D
 * Date: 05/09/2010
 * Time: 20:25:02
 * To change this template use File | Settings | File Templates.
 */
public class IDISpELCourse {
    private StandardEvaluationContext context = new StandardEvaluationContext();
    private ExpressionParser parser = new SpelExpressionParser();

    @Test
    public void simpleContextUsageTest() {
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("name", "J.J Abrams");
        String value = parser.parseExpression("'Producer name : ' + #name").getValue(context, String.class);
        System.out.println(value);
	    List<ConstructorResolver> constructorResolvers = context.getConstructorResolvers();
	    System.out.println("constructorResolvers = " + constructorResolvers);
	    TypeConverter typeConverter = context.getTypeConverter();
	    System.out.println("typeConverter = " + typeConverter);

    }


    @Test
    public void testMapPlacement() {
        Map<String, Storm> map2 = new HashMap<String, Storm>();
        map2.put("Sea", new Storm("Sea-Whirlpool", 200d, false));
        context.setVariable("seaMap", map2);
        Expression expMap = parser.parseExpression("#seaMap['Sea']");
        Storm storm = expMap.getValue(context, Storm.class);
        System.out.println(storm.getType());


        Map<String, String> map = new HashMap<String, String>();
        map.put("ObjectName", "ObjectValue");
        context.setVariable("map", map);
        Expression exp = parser.parseExpression("#map['ObjectName']");
        String value = exp.getValue(context, String.class);
        System.out.println("ObjectValue = " + value);


        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationGeneralBeans.class);
        context.setVariable("StormsMap", applicationContext.getBean("StormsMap"));
        Expression exp2 = parser.parseExpression("#StormsMap.?[value.Type =='Blizzard']");
        Map storm2 = exp2.getValue(context, Map.class);
        System.out.println(storm2.values().iterator().next());

    }

    @Test
    public void regexExpression() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForClassOperations.class, ConfigurationGeneralBeans.class);
        context.setVariable("CocaCola", applicationContext.getBean("CocaCola"));
        Expression expression = parser.parseExpression("#CocaCola.EmailAddress matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'");
        Boolean value = expression.getValue(context, Boolean.class);
        System.out.println("value = " + value);


        System.out.println(applicationContext.getBean(RegularExpressionsOperations.class).getExpressionMatch1());
        System.out.println("Company has a valid email address : " + applicationContext.getBean(RegularExpressionsOperations.class).getCompanyEmailExpression());
        System.out.println("Company has a valid phone number : " + applicationContext.getBean(RegularExpressionsOperations.class).getCompanyPhoneNumberExpression());
        System.out.println(applicationContext.getBean(RegularExpressionsOperations.class).getValidEmailExpression());
    }


    @Test
    public void constructorInvocationTest() {
        parser.parseExpression("new Inventor('Albert Einstein')").getValue();
        Inventor inventor = parser.parseExpression("new Inventor('Albert Einstein')").getValue(Inventor.class);
        parser.parseExpression("new Double(3d)");
        parser.parseExpression("new String(3.0d)");

        Fruit fruit = (Fruit) parser.parseExpression("new org.springframework.expression.spel.testresources.Fruit(5,'a','b','c')").getValue();
        System.out.println("fruit = " + fruit.getName());
        System.out.println("fruit = " + fruit.getColorName());
        System.out.println("fruit = " + fruit.getColor());
    }

    @Test
    public void memberAccessTest() {
        Fruit fruit = new Fruit("Apple-", new Color(100, 255, 0), "light green");
        Fruit fruit2 = new Fruit("Apple", new Color(255, 100, 0), "light red");
        Fruit fruit3 = new Fruit("Apricot", new Color(159, 0, 0), "dark orange");
        StandardEvaluationContext context = new StandardEvaluationContext(fruit);
        StandardEvaluationContext context2 = new StandardEvaluationContext(fruit2);
        StandardEvaluationContext context3 = new StandardEvaluationContext(fruit3);
        Expression expression = parser.parseExpression("name  + colorName");
        Expression expression2 = parser.parseExpression("Name");
        Expression expression3 = parser.parseExpression("getName()");

        System.out.println(expression.getValue(context));
        System.out.println(expression2.getValue(context2));
        System.out.println(expression3.getValue(context3));
    }

    @Test
    public void beanPropertyAccessOperationParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
        System.out.println(applicationContext.getBean(BeanPropertyGetSetOperations.class).getPropertyAccessText());
        System.out.println(applicationContext.getBean(BeanPropertyGetSetOperations.class).getPropertyAccessText2());
        System.out.println(applicationContext.getBean(BeanPropertyGetSetOperations.class).getPropertyAccessText3());
        System.out.println(applicationContext.getBean(BeanPropertyGetSetOperations.class).getHurricaneDistance());
        System.out.println(applicationContext.getBean(BeanPropertyGetSetOperations.class).getHurricaneAfterChangesMade());

    }


    @Test
    public void arithmeticOperationParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
        System.out.println(applicationContext.getBean(ArithmeticOperations.class).getTryAFloat());
        System.out.println(applicationContext.getBean(ArithmeticOperations.class).getTryADouble());
        System.out.println(applicationContext.getBean(ArithmeticOperations.class).getTryAComplexIntegerAutoConverted());
        System.out.println(applicationContext.getBean(ArithmeticOperations.class).getTryAComplexDouble());
        System.out.println(applicationContext.getBean(ArithmeticOperations.class).getTryAComplexDouble2());
    }


    @Test
    public void relationalOperationsParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
        System.out.println(applicationContext.getBean(RelationalOperations.class).getRelationOperation1());
        System.out.println(applicationContext.getBean(RelationalOperations.class).getRelationOperation2());
        System.out.println(applicationContext.getBean(RelationalOperations.class).getRelationOperation3());
        System.out.println(applicationContext.getBean(RelationalOperations.class).getRelationOperation4());
        System.out.println(applicationContext.getBean(RelationalOperations.class).getRelationOperation5());
    }


    @Test
    public void logicalOperationsParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
        System.out.println(applicationContext.getBean(LogicalOperations.class).getHarmful());
        System.out.println(applicationContext.getBean(LogicalOperations.class).getAHurricane());
        System.out.println(applicationContext.getBean(LogicalOperations.class).getFasterThenTornado());
    }


    @Test
    public void TernaryElvisSafeNavigationParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
        System.out.println("Satellite shows that " + applicationContext.getBean(TernaryElvisAndSafeNavigationOperations.class).getStormIsComing() + " is not far");
        System.out.println("Storm wind velocity : " + applicationContext.getBean(TernaryElvisAndSafeNavigationOperations.class).getStormWindVelocity());
        System.out.println("Hurricane wind velocity : " + applicationContext.getBean(TernaryElvisAndSafeNavigationOperations.class).getHurricaneWindVelocity());
        System.out.println("Hurricane distance from coast - " + applicationContext.getBean(TernaryElvisAndSafeNavigationOperations.class).getHurricaneDistanceFromCoast());
        System.out.println("Blizzard distance from coast - " + applicationContext.getBean(TernaryElvisAndSafeNavigationOperations.class).getBlizzardDistanceFromCurrentLocation());
    }


    @Test
    public void collectionSelectionParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
        System.out.println(applicationContext.getBean(CollectionSelectionOperations.class).getStrongestWind());
        System.out.println(applicationContext.getBean(CollectionSelectionOperations.class).getFirstStrongestWind());
        System.out.println(applicationContext.getBean(CollectionSelectionOperations.class).getLastNullStormDistance());
        System.out.println(applicationContext.getBean(CollectionSelectionOperations.class).getAbcOrderedStorms());
        System.out.println(applicationContext.getBean(CollectionSelectionOperations.class).getStartWithT());
        System.out.println(applicationContext.getBean(CollectionSelectionOperations.class).getOccurredAfterYear2000());

        List<Storm> selectedStorms = applicationContext.getBean(CollectionSelectionOperations.class).getSelectedStorms();
        for (Storm selectedStorm : selectedStorms) {
            System.out.println("Storms in Distance more then a 100km : " + selectedStorm.toString());
        }
        List<Integer> evenNumbersList = applicationContext.getBean(CollectionSelectionOperations.class).getEvenNumbersList();
        System.out.println("Even numbers in the List : ");
        for (Integer integer : evenNumbersList) {
            System.out.println(integer);
        }
        System.out.println("Getting Storms without SpEL" + applicationContext.getBean(CollectionSelectionOperations.class).getOccurredAfterYear2000NoSpEL());
        System.out.println("Time it took : " + applicationContext.getBean(CollectionSelectionOperations.class).getTimeItTook());
    }

    @Test
    public void systemEnvPropsDateParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
        System.out.println(applicationContext.getBean(SystemEnvPropsDateParseOperations.class).getDbInsertDate());
        System.out.println(applicationContext.getBean(SystemEnvPropsDateParseOperations.class).getDbUpdateDate());
        System.out.println(applicationContext.getBean(SystemEnvPropsDateParseOperations.class).getDbKatrinaDate());
        System.out.println(applicationContext.getBean(SystemEnvPropsDateParseOperations.class).getComputerName());
        System.out.println(applicationContext.getBean(SystemEnvPropsDateParseOperations.class).getWhereWeWork());
        System.out.println(applicationContext.getBean(SystemEnvPropsDateParseOperations.class).getWindowsPath());
    }


    @Test
    public void mapSelectionParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
        Integer singleNumber = applicationContext.getBean(MapSelectionOperations.class).getSingleNumber();
        System.out.println("Single number : " + singleNumber);
        Map<String, Storm> mapOfStorms1 = applicationContext.getBean(MapSelectionOperations.class).getMapOfStorms();
        System.out.println("Storms with Max Wind Velocity over 100kmh : ");
        for (String stormKey : mapOfStorms1.keySet()) {
            System.out.println(mapOfStorms1.get(stormKey));
        }
        Map<String, Storm> mapOfStorms2 = applicationContext.getBean(MapSelectionOperations.class).getMapOfHarmfulStorms();
        System.out.println("First UnHarmful Storm in the Map : ");
        for (String stormKey : mapOfStorms2.keySet()) {
            System.out.println(mapOfStorms2.get(stormKey));
        }
        System.out.println("Last Storm with wind velocity over 100kmh - " + applicationContext.getBean(MapSelectionOperations.class).getLastFastStorm());
        System.out.println("Last Number in map bigger then 25 - " + applicationContext.getBean(MapSelectionOperations.class).getBiggerThen25());


    }


    @Test
    public void TOperatorParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForClassOperations.class);
        System.out.println(applicationContext.getBean(TOperatorOperations.class).getParsedNumber());
        System.out.println(applicationContext.getBean(TOperatorOperations.class).getParsedNumber2());
        System.out.println(applicationContext.getBean(TOperatorOperations.class).getFlooredWindVelocity());
        System.out.println(applicationContext.getBean(TOperatorOperations.class).getaTOperatorClass());
    }


    @Test
    public void instanceOfParsingTest() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForClassOperations.class);
        System.out.println(applicationContext.getBean(InstanceOfOpeations.class).getStorm());
        System.out.println(applicationContext.getBean(InstanceOfOpeations.class).getInteger());
        System.out.println(applicationContext.getBean(InstanceOfOpeations.class).getString());
        System.out.println(applicationContext.getBean(InstanceOfOpeations.class).getString2());

    }

	@Test
	public void environmentTest() {
		AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(ConfigurationEnvConverters.class);
	}

    @Test
    public void collectionProjectionsTest() {
//        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
	    applicationContext.register(ConfigurationForPropertyAccess.class);
	    applicationContext.refresh();
        List<String> stormsNames = applicationContext.getBean(CollectionProjectionOperations.class).getStormsNames();
        System.out.println("There were " + stormsNames.size() + "  Storms found in List");
        for (String name : stormsNames) {
            System.out.println("Storm's name : " + name);
        }
        List<Double> list = applicationContext.getBean(CollectionProjectionOperations.class).getStormsNames1();
        for (Double aDouble : list) {
            System.out.println("wind's velocity : " + aDouble);
        }
        List<Double> list2 = applicationContext.getBean(CollectionProjectionOperations.class).getStormsNames2();
        for (Double aDouble : list2) {
            System.out.println("Storm distance " + aDouble);
        }
    }

    @Test
    public void performanceTests() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigurationForPropertyAccess.class);
//        context.setVariable("Storms", applicationContext.getBean("Storms"));
        StopWatch watch = new StopWatch();
        watch.start();
        long startTime = System.nanoTime();
        context.setRootObject(applicationContext.getBean("Storms"), TypeDescriptor.valueOf(Storm.class));
        Expression exp = parser.parseExpression("#root.?[occurred != null and occurred.getTime()>=T(com.bargate.domain.spring.expression.util.DateParser).parse('01/01/2000').getTime()]");
        Expression exp2 = parser.parseExpression("#root.![#this.maxWindVelocity<100]");     // hack for 3.2.2 (erasure with unchecked assignment
        List<Double> value2 = exp.getValue(context, List.class);
	    System.out.print("Storms list maxWindVelocity<100 (boolean)=" + Arrays.asList(value2));
        long endTime = System.nanoTime();
        watch.stop();
        System.out.println("parsing using SpEL took in seconds : " + watch.getTotalTimeSeconds());
        long tookWithSpEL = endTime - startTime;
        System.out.println("parsing using SpEL took in nano-sec: " + tookWithSpEL);
        System.out.println("Getting Storms without SpEL" + applicationContext.getBean(CollectionSelectionOperations.class).getOccurredAfterYear2000NoSpEL());
        long tookWithoutSpEL = applicationContext.getBean(CollectionSelectionOperations.class).getTimeItTook();
        System.out.println("Time it took : " + tookWithoutSpEL);
        System.out.println("Using SpEL it took " + tookWithSpEL / tookWithoutSpEL + " times more then using regular Java!!!");


    }
}
